package net.wxxr.http.engine;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Iterator;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import net.wxxr.dataparse.model.HttpCode;
import net.wxxr.http.auth.IAuthSign;
import net.wxxr.http.config.HttpConnectConfig;
import net.wxxr.http.config.ResponseHeaderConfig;
import net.wxxr.http.model.pojo.HttpResult;
import net.wxxr.http.request.HttpBaseRequest;
import net.wxxr.http.request.HttpPostRequest;
import net.wxxr.http.util.CodeUtil;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.ByteArrayBuffer;

import android.util.Log;







public abstract class HttpEngine {

	protected static final int AVAILABLE_RESPONSE_HEAD = 200;
	protected static final int AVAILABLE_RESPONSE_TAIL = 300;

	protected static final int COOKIE_OUT = 599;
	protected static final int FOUCE_LOGOUT = -4;
	
	
	protected static final int RETRY_ONCE = 1;
	protected static final int RETRY_MORE = 2;
	protected HttpBaseRequest baseRequest;
	protected HttpBaseRequest baseLogon;
	
	protected HttpRequestBase requestBase;

	/** 每次读取数据数目 */
	protected final static int MAX_READ = 4096;

	protected String TAG = "";
	
	protected IAuthSign authSign;
	
	public IAuthSign getAuthSign() {
		return authSign;
	}

	public void setAuthSign(IAuthSign authSign) {
		this.authSign = authSign;
	}

	public HttpEngine(HttpBaseRequest request) {
		this.baseRequest = request;
		initTag();
	}
	//代理服务器
	private HttpHost httpHost;
	//static int count;
	
	public HttpHost getHttpHost() {
		return httpHost;
	}

	public void setHttpHost(HttpHost httpHost) {
		this.httpHost = httpHost;
	}
	
	protected HttpResult doRequest() {
		HttpResult httpResult = new HttpResult();
		HttpCode code;

		InputStream entityStream = null;
		HttpClient httpClient = null;
		try {
			//初始化requestBase
			initRequest();
			//请求取消
			if(baseRequest.isCancel()) {
				requestBase.abort();
			}
			
			// 添加请求头信息
			addRequestHeaders();

			//设置请实体数据参数
			setRequestParams();

			//签名认证的设置
			if(baseRequest.isNeedAuth()) {
				authSign.sign(requestBase);
			}
			
			//执行：发起网络请求
			httpClient = getHttpClient();
			
			HttpResponse httpResponse = httpClient.execute(requestBase);
			
			int responseCode = httpResponse.getStatusLine().getStatusCode();

			if(baseRequest.isDebug()){
				System.out.println("##########httpEngin 正确网络返回码范围[200,300)##########responseCode = " + responseCode);
			}

			// 正确网络返回码范围[200,300)
			if (responseCode >= AVAILABLE_RESPONSE_HEAD && responseCode < AVAILABLE_RESPONSE_TAIL) {
				code = HttpCode.STATUS_OK;
				httpResult.setResultCode(code);
			}else if(responseCode == COOKIE_OUT){//cookie超时(错误号码 ： 599)
				code = HttpCode.COOKIE_OUT;
				httpResult.setResultCode(code);
			}else if(responseCode == FOUCE_LOGOUT){//登录失败
				code = HttpCode.FOUCE_LOGOUT;
				httpResult.setResultCode(code);
			} else {
				code = HttpCode.ERROR_NET_ACCESS;//网络连接错误
//				entityStream = httpResponse.getEntity().getContent();
//				httpResult = get_http_result_data(entityStream, httpResponse, code, httpResult);
				httpResult.setResultCode(code);
				return httpResult;
			}

			//获取返回结果
			byte[] data = new byte[MAX_READ];
			ByteArrayBuffer byteBuf = new ByteArrayBuffer(MAX_READ);

			entityStream = httpResponse.getEntity().getContent();
//			get_http_result_data(entityStream, httpResponse, code, httpResult);
			while (!baseRequest.isCancel()) {
				int bytesRead = entityStream.read(data);
				if (bytesRead == -1) {
					break;
				}
				byteBuf.append(data, 0, bytesRead);
			}

			if (baseRequest.isCancel()) {
				code = HttpCode.USER_CANCELLED;
				httpResult.setResultCode(code);
				return httpResult;
			}
			
			byte[] retData = null;
			
			if (baseRequest.isGzip()) {//解压数据
				retData = CodeUtil.gzipDecoder(byteBuf.toByteArray());
				httpResult.setData(retData);
			} else {
				retData = byteBuf.toByteArray();
				httpResult.setData(retData);
			}
			
			//设置并保持请求响应的报文头信息//保存响应消息头信息,从服务器上获得Cookie值()这里目前针对手动登陆和自动登陆进行保存记录Cookie值
//			SettingSaveResponseHeaders(httpResult, httpResponse);
			
			if(retData != null) {
				if(baseRequest.isDebug()) {
					System.out.println(new String("00#############服务器响应结果数据 byte[]############ = " + retData.toString()));
				}
			}

		}catch (ConnectTimeoutException e) {
			code = HttpCode.ERROR_CONNECTION_TIMEOUT;
			httpResult.setResultCode(code);
		} catch (SocketTimeoutException e) {
			code = HttpCode.ERROR_SOKET_TIMEOUT;
			httpResult.setResultCode(code);
		} catch (OutOfMemoryError e) {
			code = HttpCode.ERROR_NET_ACCESS;
			httpResult.setResultCode(code);
		} catch (IOException e){
			code = HttpCode.ERROR_NET_ACCESS;
			httpResult.setResultCode(code);
		} catch (Exception e){
			code = HttpCode.ERROR_NET_ACCESS;
			httpResult.setResultCode(code);
		} finally {
			if (entityStream != null) {
				try {
					entityStream.close();
				} catch (IOException e) {
					code = HttpCode.ERROR_NET_ACCESS;
					httpResult.setResultCode(code);
				}
			}

			if (httpClient != null) {
				ClientConnectionManager ccm = httpClient.getConnectionManager();
				if (ccm != null) {
					ccm.closeExpiredConnections();
				}
			}
		}

		return httpResult;
	}
	
	
	private HttpResult get_http_result_data(InputStream entityStream, HttpResponse httpResponse, HttpCode code, HttpResult httpResult)throws Exception{
		byte[] data = new byte[MAX_READ];
		ByteArrayBuffer byteBuf = new ByteArrayBuffer(MAX_READ);
		try {
			while (!baseRequest.isCancel()) {
				int bytesRead = entityStream.read(data);

				if (bytesRead == -1) {
					break;
				}
				byteBuf.append(data, 0, bytesRead);
			}

			if (baseRequest.isCancel()) {
				code = HttpCode.USER_CANCELLED;
				httpResult.setResultCode(code);
				return httpResult;
			}
			
			byte[] retData = null;
			
			if (baseRequest.isGzip()) {//解压数据
				retData = CodeUtil.gzipDecoder(byteBuf.toByteArray());
				httpResult.setData(retData);
			} else {
				retData = byteBuf.toByteArray();
				httpResult.setData(retData);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw e;
		}
	
		return httpResult;
	}
	
	protected void SettingSaveResponseHeaders(HttpResult httpResult,
			HttpResponse httpResponse) {
		ResponseHeaderConfig rhc = baseRequest.getResponseHeaderConfig();
		if(null != rhc && !"".equals(rhc)){
			//保存响应消息头信息,从服务器上获得Cookie值()
			if(null != rhc.getModulesTypeTag() && (((HttpPostRequest)baseRequest).getBodyData().contains(rhc.getModulesTypeTag()))){
				String resHeaderField = rhc.getSettingResponseHeaderField();//Set-Cookie
				String resHeaderkey = rhc.getSettingResponseHeaderkey();//JSESSIONID
				if(null != resHeaderkey && !"".equals(resHeaderkey) && null != resHeaderField && !"".equals(resHeaderField)){
					Header[]   headers = (Header[]) httpResponse.getHeaders(resHeaderField);
					//Header   firstHeader = (Header) httpResponse.getFirstHeader(resHeaderField);
					//Header   lastHeader = (Header) httpResponse.getLastHeader(resHeaderField);
					
					//System.out.println("***************firstHeader   "+firstHeader.getName()+"："+firstHeader.getValue());
					//System.out.println("***************lastHeader   "+lastHeader.getName()+"："+lastHeader.getValue());
					
					String jsession = null;
					String  value = null;
					 String[]  cookieArray = null;
					 for(Header h : headers){
							 value = h.getValue();
							 cookieArray = value.split(";");
							for(String s:cookieArray){
								if(s.indexOf(resHeaderkey) != -1){
									 jsession = s.substring("JSESSIONID=".length(), s.length());
									 if(baseRequest.isDebug()){
										 System.out.println("###############  jsession = " + jsession);
									 }
									 setResponseHeaders(jsession,rhc);
								}
							}
					 }
					 
					 
				}
			}
		}
		
	}

	
	private void setResponseHeaders(String jsession,ResponseHeaderConfig rhc) {
		baseRequest.responseHeaders.settingResponseHeaders(jsession,rhc.getModulesTypeTag());
		 
	}

	public void addRequestHeaders() {
		//动态添加消息头
		if (baseRequest.isGzip()) {
			requestBase.setHeader("Accept-Encoding", "gzip,deflate");
		}
		//处理以回调方式静态的加载公用消息头(查看TaskManagerTemp.java的静态代码块)
		if (baseRequest.getHeadParams() != null) {
			Iterator<String> iterator = baseRequest.getHeadParams().keySet().iterator();
			while (iterator.hasNext()) {
				String key = iterator.next();
				String value = baseRequest.getHeadParams().getFirst(key);
				requestBase.addHeader(key, value);
			}
		}
	}

	protected abstract void initTag();

	protected abstract void initRequest();

	protected abstract void setRequestParams() throws UnsupportedEncodingException;
	
	public HttpResult execute() throws UnsupportedEncodingException {
		int retryTime = RETRY_ONCE;
		/**
		 * 修改重试次数
		 */
		if (baseRequest.isRetry()) {
			retryTime = RETRY_MORE;
		}

		HttpResult result = new HttpResult();

		/**
		 * 检查，准备并整理所有请求的参数
		 */
		HttpCode code = baseRequest.prepareRequest();

		if (code != HttpCode.STATUS_OK) {
			result.setResultCode(code);
			return result;
		}
       // count++;
		while (retryTime > 0) {
			--retryTime;
			result = doRequest();
			if(result==null){
				if(baseRequest.isDebug()){
			    	System.out.println("#################### result == null" + baseRequest.getUrl() + "body = " + ((HttpPostRequest)baseRequest).getBodyData());
			    }
			}
			if (result != null && result.getResultCode() == HttpCode.STATUS_OK) {
				break;
			}
			//当获得session值超时，或者当前的账号被强制性在另一个设备进行登录了，从而出现了599错误，进行自动登陆操作
			/*if (result != null && result.getResultCode() == HttpCode.COOKIE_OUT) {
				 if(baseRequest.isDebug()){
				    	System.out.println("#################### requestBase = 59999");
				    }
				
				HttpBaseRequest	requestBase =  Wxxr.getInstance().autoLogo(KidConfig.IASK_LOGON + "/0", MyPrefs.getLogonString(KidApp.getInstance()), null);		
				  if(baseRequest.isDebug()){
				    	System.out.println("#################### requestBase = " + requestBase.getUrl() + "body = " + ((HttpPostRequest)requestBase).getBodyData());
				    }
				HttpResult     result1 = requestBase.executeRequest();
				
					   if(result1 != null && null != result1.getData()){
						    String logonresult=new String(result1.getData());
						    if(baseRequest.isDebug()){
						    	System.out.println("#################### logonresult = " + logonresult);
						    }
							if(logonresult.indexOf("userid")!=-1){//出现599 时，进行登录，此时表示登录成功,登录成功以后再进行请求之前的请求数据没有成功的数据
								//result = doRequest();
								if (result1 != null && result1.getResultCode() == HttpCode.STATUS_OK) {
									//--retryTime;
									//通过回调函数保存自动登陆成功的用户信息或数据状态
									requestBase.saveUserInfoForAutoLoginSuccess.saveUserInfo(logonresult);
									continue;
								}
							}else{
								result.setResultCode(HttpCode.FOUCE_LOGOUT);//登录失败
								requestBase.saveUserInfoForAutoLoginSuccess.saveUserInfo(null);
								break;
							}
					  }
			}*/
			
		}

		return result;
	}

	protected HttpClient getHttpClient()  {
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore
					.getDefaultType());
			trustStore.load(null, null);
			SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			SchemeRegistry schemeRegistry = new SchemeRegistry();
			schemeRegistry.register(new Scheme("http", PlainSocketFactory
					.getSocketFactory(), 80));
			schemeRegistry.register(new Scheme("https", sf, 443));

			HttpParams localHttpParams = new BasicHttpParams();
			HttpConnectionParams.setConnectionTimeout(localHttpParams,
					HttpConnectConfig.getConnectTimeOut());
			HttpConnectionParams.setSoTimeout(localHttpParams,
					HttpConnectConfig.getSocketTimeOut());

			ThreadSafeClientConnManager tsccm = new ThreadSafeClientConnManager(
					localHttpParams, schemeRegistry);

			HttpClient client = new DefaultHttpClient(tsccm, localHttpParams);
			client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
					httpHost);
			return client ;
		} catch (Exception e) {
			e.printStackTrace();
			Log.e("HttpUtils", "exception");
			return new DefaultHttpClient();

		}

	}

	public void cancel() {
		if(requestBase != null) {
			requestBase.abort();
		}
	}
class SSLSocketFactoryEx extends SSLSocketFactory {
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        
        public SSLSocketFactoryEx(KeyStore truststore) 
                        throws NoSuchAlgorithmException, KeyManagementException,
                        KeyStoreException, UnrecoverableKeyException {
                super(truststore);
                
                TrustManager tm = new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}  
    
            @Override  
            public void checkClientTrusted(
                            java.security.cert.X509Certificate[] chain, String authType)
                                            throws java.security.cert.CertificateException {}  
    
            @Override  
            public void checkServerTrusted(
                            java.security.cert.X509Certificate[] chain, String authType)
                                            throws java.security.cert.CertificateException {}
        };  
        sslContext.init(null, new TrustManager[] { tm }, null);  
    }  
    
    @Override  
    public Socket createSocket(Socket socket, String host, int port,boolean autoClose) throws IOException, UnknownHostException {  
            return sslContext.getSocketFactory().createSocket(socket, host, port,autoClose);  
    }  
    
    @Override  
    public Socket createSocket() throws IOException {  
        return sslContext.getSocketFactory().createSocket();  
    }  
}
}
